<!DOCTYPE html><html lang="zh-CN"><head><meta name="generator" content="Hexo 3.8.0"><meta name="google-site-verification" content="wD1H9sqEoDWmIBFt7TjlgbVqcJqiqpwR2Wq7_bvEHcE"><meta name="baidu-site-verification" content="RZErJH3v0Z"><meta charset="utf-8"><link rel="icon" href="/css/images/my_logo.png" type="image/x-icon"><title>Shiro在前后台分离架构项目中的应用 | ArtIsLong的博客</title><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1"><meta name="description" content="Shiro是Apache的强大灵活的开源安全框架，能提供认证、授权、企业会话管理、安全加密、缓存等功能。此文为解决前后台分离架构的项目下，未登录时访问系统的跳转及对应的提示信息，Shiro原有逻辑为未登录则跳转到登录Url，在前后台分离架构下，此种方式显然不能满足要求，只能通过将请求转发到一个新的Url，给出未登录提示信息，由前台去控制路由跳转到登录页面。"><meta name="keywords" content="Swagger,Shiro"><meta property="og:type" content="article"><meta property="og:title" content="Shiro在前后台分离架构项目中的应用"><meta property="og:url" content="/2018/12/15/Shiro在前后台分离架构项目中的应用/index.html"><meta property="og:site_name" content="ArtIsLong的博客"><meta property="og:description" content="Shiro是Apache的强大灵活的开源安全框架，能提供认证、授权、企业会话管理、安全加密、缓存等功能。此文为解决前后台分离架构的项目下，未登录时访问系统的跳转及对应的提示信息，Shiro原有逻辑为未登录则跳转到登录Url，在前后台分离架构下，此种方式显然不能满足要求，只能通过将请求转发到一个新的Url，给出未登录提示信息，由前台去控制路由跳转到登录页面。"><meta property="og:locale" content="zh-CN"><meta property="og:image" content="https://artislong.oss-cn-hangzhou.aliyuncs.com/images/%E5%BE%AE%E4%BF%A1%E5%85%AC%E4%BC%97%E5%8F%B7/%E5%BE%AE%E4%BF%A1%E5%85%AC%E4%BC%97%E5%8F%B71.png"><meta property="og:updated_time" content="2019-11-05T03:33:23.171Z"><meta name="twitter:card" content="summary"><meta name="twitter:title" content="Shiro在前后台分离架构项目中的应用"><meta name="twitter:description" content="Shiro是Apache的强大灵活的开源安全框架，能提供认证、授权、企业会话管理、安全加密、缓存等功能。此文为解决前后台分离架构的项目下，未登录时访问系统的跳转及对应的提示信息，Shiro原有逻辑为未登录则跳转到登录Url，在前后台分离架构下，此种方式显然不能满足要求，只能通过将请求转发到一个新的Url，给出未登录提示信息，由前台去控制路由跳转到登录页面。"><meta name="twitter:image" content="https://artislong.oss-cn-hangzhou.aliyuncs.com/images/%E5%BE%AE%E4%BF%A1%E5%85%AC%E4%BC%97%E5%8F%B7/%E5%BE%AE%E4%BF%A1%E5%85%AC%E4%BC%97%E5%8F%B71.png"><link rel="alternate" href="/atom.xml" title="ArtIsLong的博客" type="application/atom+xml"><link rel="stylesheet" href="/libs/font-awesome5/css/fontawesome.min.css"><link rel="stylesheet" href="/libs/font-awesome5/css/fa-brands.min.css"><link rel="stylesheet" href="/libs/font-awesome5/css/fa-solid.min.css"><link rel="stylesheet" href="/libs/open-sans/styles.css"><link rel="stylesheet" href="/libs/source-code-pro/styles.css"><link rel="stylesheet" href="/css/style.css"><script src="/libs/jquery/2.1.3/jquery.min.js"></script><link rel="stylesheet" href="/libs/lightgallery/css/lightgallery.min.css"><link rel="stylesheet" href="/libs/justified-gallery/justifiedGallery.min.css"><script>var _hmt=_hmt||[];!function(){var e=document.createElement("script");e.src="//hm.baidu.com/hm.js?b59777544dd5d59ce94e191bac62427c";var t=document.getElementsByTagName("script")[0];t.parentNode.insertBefore(e,t)}()</script><script>!function(){var t=document.createElement("script"),e=window.location.protocol.split(":")[0];t.src="https"===e?"https://zz.bdstatic.com/linksubmit/push.js":"http://push.zhanzhang.baidu.com/push.js";var s=document.getElementsByTagName("script")[0];s.parentNode.insertBefore(t,s)}()</script><script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script><script>(adsbygoogle=window.adsbygoogle||[]).push({google_ad_client:"ca-pub-4444107320537106",enable_page_level_ads:!0})</script></head></html><body><div id="container"><header id="header"><div id="header-main" class="header-inner"><div class="outer"><a href="/" id="logo"><i class="logo"></i> <span class="site-title">ArtIsLong的博客</span></a><nav id="main-nav"> <a class="main-nav-link" href="/.">首页</a> <a class="main-nav-link" href="/archives">归档</a> <a class="main-nav-link" href="/books">书架</a> <a class="main-nav-link" href="/about">关于我</a></nav><nav id="sub-nav"><div class="profile" id="profile-nav"> <a id="profile-anchor" href="javascript:;"><img class="avatar" src="/css/images/head.png"><i class="fas fa-caret-down"></i></a></div></nav><div id="search-form-wrap"><form class="search-form"> <input type="text" class="ins-search-input search-form-input" placeholder="搜索"> <button type="submit" class="search-form-submit"></button></form><div class="ins-search"><div class="ins-search-mask"></div><div class="ins-search-container"><div class="ins-input-wrapper"> <input type="text" class="ins-search-input" placeholder="想要查找什么..."><span class="ins-close ins-selectable"><i class="fas fa-times-circle"></i></span></div><div class="ins-section-wrapper"><div class="ins-section-container"></div></div></div></div><script>window.INSIGHT_CONFIG={TRANSLATION:{POSTS:"文章",PAGES:"页面",CATEGORIES:"分类",TAGS:"标签",UNTITLED:"(未命名)"},ROOT_URL:"/",CONTENT_URL:"/content.json"}</script><script src="/js/insight.js"></script></div></div></div><div id="main-nav-mobile" class="header-sub header-inner"><table class="menu outer"><tr><td><a class="main-nav-link" href="/.">首页</a></td><td><a class="main-nav-link" href="/archives">归档</a></td><td><a class="main-nav-link" href="/books">书架</a></td><td><a class="main-nav-link" href="/about">关于我</a></td><td><div class="search-form"> <input type="text" class="ins-search-input search-form-input" placeholder="搜索"></div></td></tr></table></div></header><div class="outer"><aside id="profile" class="profile-fixed"><div class="inner profile-inner"><div class="base-info profile-block"> <a href="/"><img id="avatar" src="/css/images/head.png"></a><h2 id="name">ArtIsLong</h2><h3 id="title">不吹牛，不装逼，只做最单纯的技术分享</h3><span id="location"><i class="fas fa-map-marker-alt" style="padding-right:5px"></i> Shanghai, China</span> <a id="follow" target="_blank" href="https://github.com/ArtIsLong">关注我</a></div><div class="article-info profile-block"><div class="article-info-block"> 39 <span>文章</span></div><div class="article-info-block"> 30 <span>标签</span></div></div><div class="profile-block social-links"><table><tr><td><a href="http://github.com/ArtIsLong" target="_blank" title="Github" class="tooltip"><i class="fab fa-github"></i></a></td><td> <a href="https://gitee.com/artislong" target="_blank" title="Gitee" class="tooltip"><i class="fab"><img src="https://gitee.com/artislong/artislong/widgets/widget_5.svg" style="height:25px;margin-bottom:-5px"></i></a></td><td> <a href="https://www.jianshu.com/u/46d989a94f20" target="_blank" title="简书" class="tooltip"><i class="fab"><span style="height:25px;margin-bottom:-5px;color:#ff8c00">简</span></i></a></td><td> <a href="mailto:chenmin049@163.com" target="_blank" title="E-Mail" class="tooltip"><i class="fab"><span style="height:25px;width:20px;margin-bottom:-5px;color:#1e90ff;font-size:16px">Mail</span></i></a></td><td><a href="/atom.xml" target="_blank" title="RSS" class="tooltip"><i class="fas fa-rss"></i></a></td></tr></table></div></div></aside><section id="main"><article id="post-Shiro在前后台分离架构项目中的应用" class="article article-type-post" itemscope="" itemprop="blogPost"><div class="article-inner"><header class="article-header"><h1 class="article-title" itemprop="name"> Shiro在前后台分离架构项目中的应用</h1><div class="article-meta"><div class="article-date"><i class="fas fa-calendar-alt"></i> <a href="/2018/12/15/Shiro在前后台分离架构项目中的应用/"><time datetime="2018-12-15T14:51:39.000Z" itemprop="datePublished">2018-12-15 22:51:39</time></a></div><div class="article-category"><i class="fas fa-folder"></i> <a class="article-category-link" href="/categories/Shiro/">Shiro</a></div><div class="article-tag"><i class="fas fa-tag"></i> <a class="tag-link" href="/tags/Shiro/">Shiro</a>, <a class="tag-link" href="/tags/Swagger/">Swagger</a></div><div style="font-size:12px"><span id="busuanzi_container_page_pv">阅读次数：<span id="busuanzi_value_page_pv"></span></span></div></div></header><div class="article-entry" itemprop="articleBody"><div id="toc" class="toc-article"> <strong class="toc-title">文章目录</strong><ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#与Spring-Security的比较"><span class="toc-number">1.</span> <span class="toc-text">与Spring Security的比较</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Shiro的几个关键要素"><span class="toc-number">2.</span> <span class="toc-text">Shiro的几个关键要素</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Shiro内置的过滤器"><span class="toc-number">3.</span> <span class="toc-text">Shiro内置的过滤器</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Shiro在前后台分离架构的项目中的应用"><span class="toc-number">4.</span> <span class="toc-text">Shiro在前后台分离架构的项目中的应用</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#Shiro在传统web项目中的应用与前后台分离项目中的区别"><span class="toc-number">4.1.</span> <span class="toc-text">Shiro在传统web项目中的应用与前后台分离项目中的区别</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#需要关注的几个点"><span class="toc-number">4.2.</span> <span class="toc-text">需要关注的几个点</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#具体实现"><span class="toc-number">4.3.</span> <span class="toc-text">具体实现</span></a></li></ol></li></ol></div><p>Shiro是Apache的强大灵活的开源安全框架</p><p>能提供认证、授权、企业会话管理、安全加密、缓存等功能。<a id="more"></a></p><h2 id="与Spring-Security的比较"><a href="#与Spring-Security的比较" class="headerlink" title="与Spring Security的比较"></a>与Spring Security的比较</h2><table><thead><tr><th>Apache Shiro</th><th>Spring Security</th></tr></thead><tbody><tr><td>简单灵活</td><td>复杂、笨重</td></tr><tr><td>可脱离Spring</td><td>必须依赖Spring</td></tr><tr><td>粒度较粗</td><td>粒度更细</td></tr></tbody></table><h2 id="Shiro的几个关键要素"><a href="#Shiro的几个关键要素" class="headerlink" title="Shiro的几个关键要素"></a>Shiro的几个关键要素</h2><ul><li><p>Subject</p><p>主体（官方解释，不明白为毛要命名为主体，一眼看到这么个东西让人很难理解），其实很简单，Subject就是应用和Shiro管理器交流的桥梁，基本上所有对权限的操作都是通过Subject进行的，比如登录，比如注销，Subject就可以看成是Shiro里的用户。</p></li><li><p>SecurityManager</p><p>安全管理器，所有与安全相关的操作都会由SecurityManager来处理，而且，通过查看源码可以看到，Subject的所有操作都是借助于SecurityManager来完成的，它是Shiro的核心。</p></li><li><p>Realm</p><p>域（这个概念也是比较抽象的），可以有一个或多个，Shiro中所有的安全验证数据都是由Realm提供的，而且Shiro不知道应用的权限存储以何种方式存储，所以我们一般都需要实现自己的Realm；可以这样看，Subject提供验证数据入口，Realm提供验证的数据源，而真正的验证功能由Shiro的认证器来完成。</p></li><li><p>Authenticator</p><p>认证器，负责主体认证的，即认证器都用来实现用户在什么情况下算是认证通过了。</p></li><li><p>Authrizer</p><p>授权器，或者访问控制器，用来对主体（Subject）进行授权，觉得主体有哪些操作的权限，能访问应用中的那些功能。</p></li><li><p>SessionManager</p><p>Session管理器，但是这个地方的Session与当初学习Servlet时接触到的Session基本类似，但是这个Session是由Shiro自己去维护的，与Web环境无关，可以应用到Web环境中，也可以应用到普通的JavaSE环境。</p></li><li><p>SessionDAO</p><p>数据访问对象，用于会话的CRUD，比如将Session存储到Redis，或者数据库，或者内存，都可以通过SessionDAO来实现，可以使用默认的SessionDAO，也可以自定义实现。</p></li><li><p>CacheManager</p><p>缓存控制器，用来管理用户、角色、权限等的缓存。</p></li><li><p>Cryptography</p><p>密码模块，Shiro提供了一些常见的加密组件用于密码加密/解密。</p></li></ul><h2 id="Shiro内置的过滤器"><a href="#Shiro内置的过滤器" class="headerlink" title="Shiro内置的过滤器"></a>Shiro内置的过滤器</h2><ul><li>anon,authBasic,authc,user,logout</li><li>perms,roles,ssl,port</li></ul><table><thead><tr><th>过滤器简称过滤器简称</th><th>对应的java类</th></tr></thead><tbody><tr><td>anon</td><td>org.apache.shiro.web.filter.authc.AnonymousFilter</td></tr><tr><td>authc</td><td>org.apache.shiro.web.filter.authc.FormAuthenticationFilter</td></tr><tr><td>authcBasic</td><td>org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter</td></tr><tr><td>perms</td><td>org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter</td></tr><tr><td>port</td><td>org.apache.shiro.web.filter.authz.PortFilter</td></tr><tr><td>rest</td><td>org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter</td></tr><tr><td>roles</td><td>org.apache.shiro.web.filter.authz.RolesAuthorizationFilter</td></tr><tr><td>ssl</td><td>org.apache.shiro.web.filter.authz.SslFilter</td></tr><tr><td>user</td><td>org.apache.shiro.web.filter.authc.UserFilter</td></tr><tr><td>logout</td><td>org.apache.shiro.web.filter.authc.LogoutFilter</td></tr></tbody></table><h2 id="Shiro在前后台分离架构的项目中的应用"><a href="#Shiro在前后台分离架构的项目中的应用" class="headerlink" title="Shiro在前后台分离架构的项目中的应用"></a>Shiro在前后台分离架构的项目中的应用</h2><h3 id="Shiro在传统web项目中的应用与前后台分离项目中的区别"><a href="#Shiro在传统web项目中的应用与前后台分离项目中的区别" class="headerlink" title="Shiro在传统web项目中的应用与前后台分离项目中的区别"></a>Shiro在传统web项目中的应用与前后台分离项目中的区别</h3><p>传统项目中，前后台在一个工程里，页面的跳转，请求的访问，一般都是由后台来控制，中间不需要做太多的转换。</p><p>而在前后台分离项目中，前后台在不同的工程里，也在不同的服务器上，页面的跳转由前端路由来控制（其实也没啥页面的跳转，随着前端框架如雨后竹笋一般的冒出来，前端应用都往单页面应用的方向发展），后台只负责提供数据以及安全验证，对于页面的东西后台已经不做关注。在这种情况下，在使用Shiro时就需要有一些自定义的东西了。</p><h3 id="需要关注的几个点"><a href="#需要关注的几个点" class="headerlink" title="需要关注的几个点"></a>需要关注的几个点</h3><ul><li>通过Redis存储Session</li><li>由Shiro来跳转的请求地址</li><li>配置不需要验证的请求接口</li></ul><h3 id="具体实现"><a href="#具体实现" class="headerlink" title="具体实现"></a>具体实现</h3><p>作为一个SpringBoot洗脑流，不管是什么新东西，最先想到的就是通过SpringBoot来集成。这里通过SpringBoot，集成Shiro、Swagger（模拟前台通过JSON请求后台）、Redis（暂时只存储Session），使用Swagger来模拟请求，测试Shiro的权限控制。</p><p>以下的集成相关东西，都是建立于一个完整的SpringBoot Demo。</p><ul><li><p>集成Redis</p><p>引入Redis依赖</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- Redis --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-data-redis<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><p>引入第三方Redis序列化工具</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 高效的序列化库kyro --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>com.esotericsoftware<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>kryo-shaded<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>4.0.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><p><strong>注：</strong> Kryo是一个快速高效的Java序列化框架，旨在提供快速、高效和易用的API。无论文件、数据库或网络数据Kryo都可以随时完成序列化。Kryo还可以执行自动深拷贝（克隆）、浅拷贝（克隆）。这是对象到对象的直接拷贝，非对象-&gt;字节-&gt;对象的拷贝。在后面的文章会分析一下Redis各种序列化方式的效率。</p><p>配置Redis连接（为了方便测试，使用Redis单机版即可）</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line"><span class="attr">  redis:</span></span><br><span class="line"><span class="attr">    database:</span> <span class="number">0</span></span><br><span class="line"><span class="attr">    host:</span> <span class="string">localhost</span></span><br><span class="line"><span class="attr">    password:</span>  <span class="comment"># Redis服务器若设置密码，此处必须配置</span></span><br><span class="line"><span class="attr">    port:</span> <span class="number">6379</span></span><br><span class="line"><span class="attr">    timeout:</span> <span class="number">10000</span> <span class="comment"># 连接超时时间（毫秒）</span></span><br><span class="line"><span class="attr">    pool:</span></span><br><span class="line"><span class="attr">      max-active:</span> <span class="number">8</span> <span class="comment"># 连接池最大连接数（使用负数表示没有限制）</span></span><br><span class="line"><span class="attr">      max-idle:</span> <span class="number">8</span> <span class="comment"># 连接池中的最大空闲连接</span></span><br><span class="line"><span class="attr">      min-idle:</span> <span class="number">0</span> <span class="comment"># 连接池中的最小空闲连接</span></span><br><span class="line"><span class="attr">      max-wait:</span> <span class="bullet">-1</span> <span class="comment"># 连接池最大阻塞等待时间（使用负数表示没有限制）</span></span><br></pre></td></tr></table></figure></li><li><p>Swagger的集成</p><p>为了不重复造轮子，使用<a href="http://blog.didispace.com/spring-boot-starter-swagger-1.3.0/" target="_blank" rel="noopener">swagger-spring-boot-starter</a>（一个大牛自己针对Swagger封装的一个SpringBoot的Starter自动配置模块）即可。</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- swagger API集成 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>com.spring4all<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>swagger-spring-boot-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.7.1.RELEASE<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><p>在使用Shiro之后，由于默认情况下，资源都会被Shiro拦截，所以需要对Swagger的资源手动做加载，并使用<code>@EnableSwagger2Doc</code>打开Swagger自动配置，并且在下面shiro拦截器配置时，将swagger相关资源配置为anno。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@EnableSwagger</span>2Doc</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SwaggerConfiguration</span> <span class="keyword">extends</span> <span class="title">WebMvcConfigurerAdapter</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">addResourceHandlers</span><span class="params">(ResourceHandlerRegistry registry)</span> </span>&#123;</span><br><span class="line">        registry.addResourceHandler(<span class="string">"/js/**"</span>).addResourceLocations(<span class="string">"classpath:/js/"</span>);</span><br><span class="line">        registry.addResourceHandler(<span class="string">"swagger-ui.html"</span>)</span><br><span class="line">                .addResourceLocations(<span class="string">"classpath:/META-INF/resources/"</span>);</span><br><span class="line">        registry.addResourceHandler(<span class="string">"/webjars/**"</span>)</span><br><span class="line">                .addResourceLocations(<span class="string">"classpath:/META-INF/resources/webjars/"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>配置Swagger</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">swagger:</span></span><br><span class="line"><span class="attr">  title:</span> <span class="string">测试Demo</span></span><br><span class="line"><span class="attr">  description:</span> <span class="string">测试Demo</span></span><br><span class="line"><span class="attr">  version:</span> <span class="number">1.0</span><span class="string">.RELEASE</span></span><br><span class="line"><span class="attr">  license:</span> <span class="string">Apache</span> <span class="string">License,</span> <span class="string">Version</span> <span class="number">2.0</span></span><br><span class="line"><span class="attr">  license-url:</span> <span class="attr">https://www.apache.org/licenses/LICENSE-2.0.html</span></span><br><span class="line"><span class="attr">  terms-of-service-url:</span> <span class="attr">https://github.com/dyc87112/spring-boot-starter-swagger</span></span><br><span class="line"><span class="attr">  base-package:</span> <span class="string">com.example</span></span><br><span class="line"><span class="attr">  base-path:</span> <span class="string">/**</span></span><br><span class="line"><span class="attr">  exclude-path:</span> <span class="string">/error,</span> <span class="string">/ops/**</span></span><br></pre></td></tr></table></figure></li><li><p>Shiro集成</p><p>引入Shiro官方提供的与Spring类项目集成的依赖包</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- shiro begin --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.shiro<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>shiro-spring<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>$&#123;shiro.version&#125;<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- shiro ehcache --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.shiro<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>shiro-ehcache<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>$&#123;shiro.version&#125;<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><p>除了上面这两个依赖包之外，以便于以后项目做集群，使用Redis存储Shiro的安全验证信息，所以在Github上翻了翻，找到了下面shiro-redis包，它很好的完成了Redis与Shiro的集成，不需要开发人员自己去编码，实现Shiro的SessionDAO接口。</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- shiro与Redis整合的开源插件 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.crazycake<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>shiro-redis<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>3.0.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><p>还没完，Shiro的常规配置还需要通过JavaConfig的方式去配置（以SpringBoot自动配置的方式实现），废话少说，下面代码见真章。</p><p>shiro的相关拦截规则配置</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">security:</span></span><br><span class="line"><span class="attr">  shiro:</span></span><br><span class="line"><span class="attr">    filter:</span></span><br><span class="line"><span class="attr">      anon:</span>   <span class="comment"># 不需要Shiro拦截的请求URL</span></span><br><span class="line"><span class="bullet">        -</span> <span class="string">/api/v1/**</span>  <span class="comment"># swagger接口文档</span></span><br><span class="line"><span class="bullet">        -</span> <span class="string">/swagger-ui.html</span></span><br><span class="line"><span class="bullet">        -</span> <span class="string">/webjars/**</span></span><br><span class="line"><span class="bullet">        -</span> <span class="string">/swagger-resources/**</span></span><br><span class="line"><span class="bullet">        -</span> <span class="string">/user/login</span>   <span class="comment"># 登录接口</span></span><br><span class="line"><span class="bullet">        -</span> <span class="string">/user/noLogin</span>   <span class="comment"># 未登录提示信息接口</span></span><br><span class="line"><span class="attr">      authc:</span>   <span class="comment"># 需要Shiro拦截的请求URL</span></span><br><span class="line"><span class="bullet">        -</span> <span class="string">/**</span></span><br><span class="line"><span class="attr">    loginUrl:</span> <span class="string">/user/login</span>   <span class="comment"># 登录接口</span></span><br><span class="line"><span class="attr">    noAccessUrl:</span> <span class="string">/user/noLogin</span>   <span class="comment"># 未登录时跳转URL</span></span><br><span class="line"><span class="attr">    globalSessionTimeout:</span> <span class="number">30</span>  <span class="comment"># 登录过期时长</span></span><br></pre></td></tr></table></figure><p>自定义的Shiro属性配置类<code>ShiroProperties.java</code></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="meta">@ConfigurationProperties</span>(prefix = <span class="string">"security.shiro"</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ShiroProperties</span> </span>&#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 登录Url</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> String loginUrl;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 没权限访问时的转发Url(做未登录提示信息用)</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> String noAccessUrl;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Shiro请求拦截规则配置(Shiro的拦截器规则，常用的anon和authc)</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> Map&lt;String, List&lt;String&gt;&gt; filter;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Shiro Session 过期时间（分钟）</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> Long globalSessionTimeout = <span class="number">30L</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>为解决前后台分离架构的项目下，未登录时访问系统的跳转及对应的提示信息Shiro原有逻辑为未登录则跳转到登录Url，在前后台分离架构下，此种方式显然不能满足要求，只能修改authc默认过滤器处理流程，通过将请求转发到一个新的Url，给出未登录提示信息，由前台去控制路由跳转到登录页面</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Slf</span>4j</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SelfDefinedFormAuthenticationFilter</span> <span class="keyword">extends</span> <span class="title">FormAuthenticationFilter</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 没有权限访问的提示信息跳转URL</span></span><br><span class="line">    <span class="keyword">private</span> String noAccessUrl;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">getNoAccessUrl</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> noAccessUrl;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> SelfDefinedFormAuthenticationFilter <span class="title">setNoAccessUrl</span><span class="params">(String noAccessUrl)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.noAccessUrl = noAccessUrl;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 重写跳转到登录URL的逻辑，改为转发到未登录URL</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">redirectToLogin</span><span class="params">(ServletRequest request, ServletResponse response)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        String noAccessUrl = getNoAccessUrl();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            request.getRequestDispatcher(noAccessUrl).forward(request, response);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (ServletException e) &#123;</span><br><span class="line">            e.getMessage();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>自定义Realm，提供登录验证数据及授权逻辑</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Slf</span>4j</span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SelfDefinedShiroRealm</span> <span class="keyword">extends</span> <span class="title">AuthorizingRealm</span> </span>&#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 授权</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> principals</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> AuthorizationInfo <span class="title">doGetAuthorizationInfo</span><span class="params">(PrincipalCollection principals)</span> </span>&#123;</span><br><span class="line">        SimpleAuthorizationInfo authorizationInfo = <span class="keyword">new</span> SimpleAuthorizationInfo();</span><br><span class="line">        <span class="keyword">return</span> authorizationInfo;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 认证</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> token</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> AuthenticationException</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> AuthenticationInfo <span class="title">doGetAuthenticationInfo</span><span class="params">(AuthenticationToken token)</span></span></span><br><span class="line"><span class="function">            <span class="keyword">throws</span> AuthenticationException </span>&#123;</span><br><span class="line">        String username = (String) token.getPrincipal();</span><br><span class="line">        log.info(username);</span><br><span class="line">        SimpleAuthenticationInfo authorizationInfo = <span class="keyword">new</span> SimpleAuthenticationInfo(</span><br><span class="line">                <span class="keyword">new</span> User(username, <span class="string">"123"</span>),</span><br><span class="line">                username,</span><br><span class="line">                getName()</span><br><span class="line">        );</span><br><span class="line">        <span class="keyword">return</span> authorizationInfo;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>新建配置类，配置Shiro相关配置。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@EnableConfigurationProperties</span>(ShiroProperties.class)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ShiroConfiguration</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> RedisProperties redisProperties;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> ShiroProperties shiroProperties;</span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> ShiroFilterFactoryBean <span class="title">shiroFilterFactoryBean</span><span class="params">(SecurityManager securityManager)</span> </span>&#123;</span><br><span class="line">        ShiroFilterFactoryBean shiroFilterFactoryBean = <span class="keyword">new</span> ShiroFilterFactoryBean();</span><br><span class="line">        <span class="comment">//获取filters</span></span><br><span class="line">        Map&lt;String, Filter&gt; filters = shiroFilterFactoryBean.getFilters();</span><br><span class="line">        <span class="comment">//将自定义 的FormAuthenticationFilter注入shiroFilter中</span></span><br><span class="line">        filters.put(<span class="string">"authc"</span>, <span class="keyword">new</span> SelfDefinedFormAuthenticationFilter().</span><br><span class="line">                setNoAccessUrl(shiroProperties.getNoAccessUrl()));</span><br><span class="line">        shiroFilterFactoryBean.setSecurityManager(securityManager);</span><br><span class="line">        Map&lt;String, String&gt; filterChainDefinitionMap = <span class="keyword">new</span> LinkedHashMap&lt;String, String&gt;();</span><br><span class="line">        <span class="comment">//注意过滤器配置顺序 不能颠倒</span></span><br><span class="line">        Map&lt;String, List&lt;String&gt;&gt; filterMap = shiroProperties.getFilter();</span><br><span class="line">        filterMap.forEach((filter, urls) -&gt; &#123;</span><br><span class="line">            urls.forEach(url -&gt; &#123;</span><br><span class="line">                filterChainDefinitionMap.put(url, filter);</span><br><span class="line">            &#125;);</span><br><span class="line">        &#125;);</span><br><span class="line">        <span class="comment">// 配置shiro默认登录界面地址，前后端分离中登录界面跳转应由前端路由控制，后台仅返回json数据</span></span><br><span class="line">shiroFilterFactoryBean.setLoginUrl(shiroProperties.getLoginUrl());</span><br><span class="line">shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);</span><br><span class="line">        <span class="keyword">return</span> shiroFilterFactoryBean;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 凭证匹配器(密码需要加密时，可使用)</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> HashedCredentialsMatcher <span class="title">hashedCredentialsMatcher</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        HashedCredentialsMatcher hashedCredentialsMatcher = <span class="keyword">new</span> HashedCredentialsMatcher();</span><br><span class="line">        <span class="comment">// 设置加密算法 Md5Hash</span></span><br><span class="line">        hashedCredentialsMatcher.setHashAlgorithmName(<span class="string">"md5"</span>);</span><br><span class="line">        <span class="comment">// 设置散列加密次数 如：2=md5(md5(aaa))</span></span><br><span class="line">        hashedCredentialsMatcher.setHashIterations(<span class="number">2</span>);</span><br><span class="line">        <span class="keyword">return</span> hashedCredentialsMatcher;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> SecurityManager <span class="title">securityManager</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">            AuthorizingRealm authorizingRealm,</span></span></span><br><span class="line"><span class="function"><span class="params">            SessionManager sessionManager,</span></span></span><br><span class="line"><span class="function"><span class="params">            RedisCacheManager redisCacheManager)</span> </span>&#123;</span><br><span class="line">        DefaultWebSecurityManager securityManager = <span class="keyword">new</span> DefaultWebSecurityManager();</span><br><span class="line">        securityManager.setRealm(authorizingRealm);</span><br><span class="line">        <span class="comment">// 自定义的Session管理</span></span><br><span class="line">        securityManager.setSessionManager(sessionManager);</span><br><span class="line">        <span class="comment">// 自定义的缓存实现</span></span><br><span class="line">        securityManager.setCacheManager(redisCacheManager);</span><br><span class="line">        <span class="keyword">return</span> securityManager;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 自定义的SessionManager</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> redisSessionDAO</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> SessionManager <span class="title">sessionManager</span><span class="params">(RedisSessionDAO redisSessionDAO)</span> </span>&#123;</span><br><span class="line">        SelfDefinedSessionManager sessionManager = <span class="keyword">new</span> SelfDefinedSessionManager();</span><br><span class="line">        sessionManager.setSessionDAO(redisSessionDAO);     		    sessionManager.setGlobalSessionTimeout(shiroProperties.getGlobalSessionTimeout() * <span class="number">60</span> * <span class="number">1000</span>);</span><br><span class="line">        <span class="keyword">return</span> sessionManager;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 配置shiro redisManager</span></span><br><span class="line"><span class="comment">     * 使用的是shiro-redis开源插件</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> RedisManager <span class="title">redisManager</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        RedisManager redisManager = <span class="keyword">new</span> RedisManager();</span><br><span class="line">        redisManager.setHost(redisProperties.getHost());</span><br><span class="line">        redisManager.setPort(redisProperties.getPort());</span><br><span class="line">        redisManager.setTimeout(redisProperties.getTimeout());</span><br><span class="line">        <span class="keyword">if</span> (!ObjectUtils.isEmpty(redisProperties.getPassword())) &#123;</span><br><span class="line">            redisManager.setPassword(redisProperties.getPassword());</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> redisManager;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * cacheManager 缓存 redis实现</span></span><br><span class="line"><span class="comment">     * 使用的是shiro-redis开源插件</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> redisManager</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> RedisCacheManager <span class="title">redisCacheManager</span><span class="params">(RedisManager redisManager)</span> </span>&#123;</span><br><span class="line">        RedisCacheManager redisCacheManager = <span class="keyword">new</span> RedisCacheManager();</span><br><span class="line">        redisCacheManager.setRedisManager(redisManager);</span><br><span class="line">        redisCacheManager.setValueSerializer(<span class="keyword">new</span> StringSerializer());</span><br><span class="line">        <span class="keyword">return</span> redisCacheManager;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * RedisSessionDAO shiro sessionDao层的实现 redis实现</span></span><br><span class="line"><span class="comment">     * 使用的是shiro-redis开源插件</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> redisManager</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> RedisSessionDAO <span class="title">redisSessionDAO</span><span class="params">(RedisManager redisManager)</span> </span>&#123;</span><br><span class="line">        RedisSessionDAO redisSessionDAO = <span class="keyword">new</span> RedisSessionDAO();</span><br><span class="line">        redisSessionDAO.setRedisManager(redisManager);</span><br><span class="line">        <span class="keyword">return</span> redisSessionDAO;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 开启shiro aop注解支持</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> securityManager</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> AuthorizationAttributeSourceAdvisor <span class="title">authorizationAttributeSourceAdvisor</span><span class="params">(SecurityManager securityManager)</span> </span>&#123;</span><br><span class="line">        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor =</span><br><span class="line">                <span class="keyword">new</span> AuthorizationAttributeSourceAdvisor();</span><br><span class="line">      authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);</span><br><span class="line">        <span class="keyword">return</span> authorizationAttributeSourceAdvisor;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>编写简单的Controller，测试一下</p><p>UserController.java</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Autowired</span></span><br><span class="line">   <span class="keyword">private</span> RedisSessionDAO redisSessionDAO;</span><br><span class="line">   <span class="meta">@ApiOperation</span>(<span class="string">"登录"</span>)</span><br><span class="line">   <span class="meta">@PostMapping</span>(<span class="string">"/login"</span>)</span><br><span class="line">   <span class="function"><span class="keyword">public</span> Object <span class="title">login</span><span class="params">(@RequestBody User user)</span> </span>&#123;</span><br><span class="line">       Subject subject = SecurityUtils.getSubject();</span><br><span class="line">       UsernamePasswordToken token = <span class="keyword">new</span> UsernamePasswordToken(user.getUserName(), user.getPassword());</span><br><span class="line">       <span class="keyword">try</span> &#123;</span><br><span class="line">           <span class="comment">// 登录</span></span><br><span class="line">           subject.login(token);</span><br><span class="line">           <span class="comment">// 登录成功后，获取菜单权限信息</span></span><br><span class="line">           <span class="keyword">if</span> (subject.isAuthenticated()) &#123;</span><br><span class="line">               <span class="keyword">return</span> <span class="string">"登录成功"</span>;</span><br><span class="line">           &#125;</span><br><span class="line">       &#125; <span class="keyword">catch</span> (IncorrectCredentialsException e) &#123;</span><br><span class="line">           <span class="keyword">return</span> <span class="string">"密码错误"</span>;</span><br><span class="line">       &#125; <span class="keyword">catch</span> (LockedAccountException e) &#123;</span><br><span class="line">           <span class="keyword">return</span> <span class="string">"登录失败，该用户已被冻结"</span>;</span><br><span class="line">       &#125; <span class="keyword">catch</span> (AuthenticationException e) &#123;</span><br><span class="line">           <span class="keyword">return</span> <span class="string">"该用户不存在"</span>;</span><br><span class="line">       &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">           <span class="keyword">return</span> e.getMessage();</span><br><span class="line">       &#125;</span><br><span class="line">       <span class="keyword">return</span> <span class="string">"登录失败"</span>;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="meta">@ApiOperation</span>(<span class="string">"注销"</span>)</span><br><span class="line">   <span class="meta">@PostMapping</span>(<span class="string">"/logout"</span>)</span><br><span class="line">   <span class="function"><span class="keyword">public</span> Object <span class="title">logout</span><span class="params">()</span> </span>&#123;</span><br><span class="line">       Subject subject = SecurityUtils.getSubject();</span><br><span class="line">       redisSessionDAO.delete(subject.getSession());</span><br><span class="line">       <span class="keyword">return</span> <span class="string">"注销成功"</span>;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="meta">@ApiOperation</span>(<span class="string">"未登录提示信息接口"</span>)</span><br><span class="line">   <span class="meta">@RequestMapping</span>(<span class="string">"/noLogin"</span>)</span><br><span class="line">   <span class="function"><span class="keyword">public</span> Object <span class="title">noLogin</span><span class="params">()</span> </span>&#123;</span><br><span class="line">       <span class="keyword">return</span> <span class="string">"未登录，请先登录再访问"</span>;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="meta">@ApiOperation</span>(<span class="string">"需登录才能访问"</span>)</span><br><span class="line">   <span class="meta">@PostMapping</span>(<span class="string">"/home"</span>)</span><br><span class="line">   <span class="function"><span class="keyword">public</span> Object <span class="title">home</span><span class="params">()</span> </span>&#123;</span><br><span class="line">       <span class="keyword">return</span> <span class="string">"这是主页"</span>;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><p>访问<a href="http://localhost:8080/shiro/swagger-ui.html页面，通过Swagger测试请求的拦截。" target="_blank" rel="noopener">http://localhost:8080/shiro/swagger-ui.html页面，通过Swagger测试请求的拦截。</a></p><ol><li><p>未登录访问/user/home</p><p>返回信息<code>“未登录，请先登录再访问”</code>，代表请求成功拦截到了，未登录不能正常访问系统</p></li><li><p>访问/user/login进行登录，然后访问/user/home</p><p>入参:</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="attr">"userName"</span>:<span class="string">"admin"</span>,</span><br><span class="line">    <span class="attr">"password"</span>:<span class="string">"123"</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>出参:</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">"登录成功"</span></span><br></pre></td></tr></table></figure><p>然后访问/user/home，成功返回<code>&quot;这是主页&quot;</code></p></li><li><p>注销后在访问/user/home</p><p>直接请求/user/logout，访问/user/home，提示<code>“未登录，请先登录再访问”</code>，表示成功注销。</p></li></ol><p><strong>注：</strong> /user/noLogin使用的是<code>@RequestMapping(&quot;/noLogin&quot;)</code>，是为了保证所有请求方式(GET/POST/PUT/DELETE等)的未登录请求都能转发到此接口，从而正确返回未登录提示信息。</p></li></ul><p>以上相关源码，请访问<a href="https://github.com/ArtIsLong/shiro-spring-boot-starter.git" target="_blank" rel="noopener">https://github.com/ArtIsLong/shiro-spring-boot-starter.git</a></p><hr><p>关注我的微信公众号：FramePower<br>我会不定期发布相关技术积累，欢迎对技术有追求、志同道合的朋友加入，一起学习成长！</p><hr><p><img src="https://artislong.oss-cn-hangzhou.aliyuncs.com/images/%E5%BE%AE%E4%BF%A1%E5%85%AC%E4%BC%97%E5%8F%B7/%E5%BE%AE%E4%BF%A1%E5%85%AC%E4%BC%97%E5%8F%B71.png" alt="微信公众号"></p><div class="reward"><div class="reward-button">赏 <span class="reward-code"><span class="alipay-code"><img class="alipay-img wdp-appear" src="/css/images/alipay.jpeg"></span> <span class="wechat-code"><img class="wechat-img wdp-appear" src="/css/images/Wechat.jpeg"></span></span></div><p class="reward-notice">如果文章对你有帮助，欢迎点击上方按钮打赏作者</p></div></div><footer class="article-footer"><div class="share-container"><div class="bdsharebuttonbox"> <a href="#" class="bds_more" data-cmd="more">分享到：</a> <a href="#" class="bds_qzone" data-cmd="qzone" title="分享到QQ空间">QQ空间</a> <a href="#" class="bds_tsina" data-cmd="tsina" title="分享到新浪微博">新浪微博</a> <a href="#" class="bds_tqq" data-cmd="tqq" title="分享到腾讯微博">腾讯微博</a> <a href="#" class="bds_renren" data-cmd="renren" title="分享到人人网">人人网</a> <a href="#" class="bds_weixin" data-cmd="weixin" title="分享到微信">微信</a></div><script>with(window._bd_share_config={common:{bdSnsKey:{},bdText:"",bdMini:"2",bdMiniList:!1,bdPic:"",bdStyle:"0",bdSize:"16"},share:{bdSize:16}},document)(getElementsByTagName("head")[0]||body).appendChild(createElement("script")).src="http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion="+~(-new Date/36e5)</script><style>.bdshare_popup_box{border-radius:4px;border:#e1e1e1 solid 1px}.bdshare-button-style0-16 .bds_more,.bdshare-button-style0-16 a{padding-left:20px;margin:6px 10px 6px 0}.bdshare_dialog_list a,.bdshare_popup_bottom a,.bdshare_popup_list a{font-family:'Microsoft Yahei'}.bdshare_popup_top{display:none}.bdshare_popup_bottom{height:auto;padding:5px}</style></div></footer></div><nav id="article-nav"> <a href="/2018/12/20/Java基础面试题（只有一题）/" id="article-nav-newer" class="article-nav-link-wrap"><strong class="article-nav-caption">上一篇</strong><div class="article-nav-title"> Java基础-面试题</div></a> <a href="/2018/12/01/Swagger常用注解/" id="article-nav-older" class="article-nav-link-wrap"><strong class="article-nav-caption">下一篇</strong><div class="article-nav-title">Swagger常用注解</div></a></nav></article><section id="comments"></section></section><aside id="sidebar"><div class="widget-wrap"><h3 class="widget-title">最新文章</h3><div class="widget"><ul id="recent-post" class=""><li><div class="item-thumbnail"><a href="/2019/07/14/SpringBoot完美整合Jfinal/" class="thumbnail"><span class="thumbnail-image thumbnail-none"></span></a></div><div class="item-inner"><p class="item-category"><a class="article-category-link" href="/categories/SpringBoot/">SpringBoot</a></p><p class="item-title"><a href="/2019/07/14/SpringBoot完美整合Jfinal/" class="title">SpringBoot完美整合Jfinal</a></p><p class="item-date"><time datetime="2019-07-14T15:32:31.000Z" itemprop="datePublished">2019-07-14 23:32:31</time></p></div></li><li><div class="item-thumbnail"><a href="/2019/06/27/SpringBoot集成Flowable-UI/" class="thumbnail"><span class="thumbnail-image thumbnail-none"></span></a></div><div class="item-inner"><p class="item-category"><a class="article-category-link" href="/categories/工作流/">工作流</a><i class="fas fa-angle-right"></i><a class="article-category-link" href="/categories/工作流/Flowable/">Flowable</a></p><p class="item-title"><a href="/2019/06/27/SpringBoot集成Flowable-UI/" class="title">SpringBoot集成Flowable UI</a></p><p class="item-date"><time datetime="2019-06-26T16:27:21.000Z" itemprop="datePublished">2019-06-27 00:27:21</time></p></div></li><li><div class="item-thumbnail"><a href="/2019/05/25/Flowable五个引擎及相关服务总结/" class="thumbnail"><span class="thumbnail-image thumbnail-none"></span></a></div><div class="item-inner"><p class="item-category"><a class="article-category-link" href="/categories/工作流/">工作流</a><i class="fas fa-angle-right"></i><a class="article-category-link" href="/categories/工作流/Flowable/">Flowable</a></p><p class="item-title"><a href="/2019/05/25/Flowable五个引擎及相关服务总结/" class="title">Flowable五个引擎及相关服务总结</a></p><p class="item-date"><time datetime="2019-05-25T15:43:07.000Z" itemprop="datePublished">2019-05-25 23:43:07</time></p></div></li><li><div class="item-thumbnail"><a href="/2019/03/01/微服务基础概念认知总结/" class="thumbnail"><span class="thumbnail-image thumbnail-none"></span></a></div><div class="item-inner"><p class="item-category"><a class="article-category-link" href="/categories/思考/">思考</a></p><p class="item-title"><a href="/2019/03/01/微服务基础概念认知总结/" class="title">微服务基础概念认知总结</a></p><p class="item-date"><time datetime="2019-03-01T15:17:33.000Z" itemprop="datePublished">2019-03-01 23:17:33</time></p></div></li><li><div class="item-thumbnail"><a href="/2019/03/01/Oracle-SQL优化总结/" class="thumbnail"><span class="thumbnail-image thumbnail-none"></span></a></div><div class="item-inner"><p class="item-category"><a class="article-category-link" href="/categories/Oracle/">Oracle</a></p><p class="item-title"><a href="/2019/03/01/Oracle-SQL优化总结/" class="title">Oracle SQL优化总结</a></p><p class="item-date"><time datetime="2019-03-01T07:30:47.000Z" itemprop="datePublished">2019-03-01 15:30:47</time></p></div></li></ul></div></div><div class="widget-wrap"><h3 class="widget-title">分类</h3><div class="widget"><ul class="category-list"><li class="category-list-item"><a class="category-list-link" href="/categories/Alibaba/">Alibaba</a><span class="category-list-count">1</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/Docker/">Docker</a><span class="category-list-count">3</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/Git/">Git</a><span class="category-list-count">1</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/Java基础/">Java基础</a><span class="category-list-count">4</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/Nginx/">Nginx</a><span class="category-list-count">3</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/Oracle/">Oracle</a><span class="category-list-count">1</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/Redis/">Redis</a><span class="category-list-count">2</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/Shiro/">Shiro</a><span class="category-list-count">1</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/Spring/">Spring</a><span class="category-list-count">2</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/SpringBoot/">SpringBoot</a><span class="category-list-count">7</span><ul class="category-list-child"><li class="category-list-item"><a class="category-list-link" href="/categories/SpringBoot/Shiro/">Shiro</a><span class="category-list-count">1</span></li></ul></li><li class="category-list-item"><a class="category-list-link" href="/categories/Storm/">Storm</a><span class="category-list-count">1</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/Tomcat/">Tomcat</a><span class="category-list-count">2</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/Zookeeper/">Zookeeper</a><span class="category-list-count">1</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/工作流/">工作流</a><span class="category-list-count">3</span><ul class="category-list-child"><li class="category-list-item"><a class="category-list-link" href="/categories/工作流/Activiti/">Activiti</a><span class="category-list-count">1</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/工作流/Flowable/">Flowable</a><span class="category-list-count">2</span></li></ul></li><li class="category-list-item"><a class="category-list-link" href="/categories/工具/">工具</a><span class="category-list-count">1</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/开发工具框架/">开发工具框架</a><span class="category-list-count">3</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/思考/">思考</a><span class="category-list-count">2</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/服务器/">服务器</a><span class="category-list-count">1</span></li></ul></div></div><div class="widget-wrap"><h3 class="widget-title">归档</h3><div class="widget"><ul class="archive-list"><li class="archive-list-item"><a class="archive-list-link" href="/archives/2019/07/">七月 2019</a><span class="archive-list-count">1</span></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2019/06/">六月 2019</a><span class="archive-list-count">1</span></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2019/05/">五月 2019</a><span class="archive-list-count">1</span></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2019/03/">三月 2019</a><span class="archive-list-count">2</span></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2019/02/">二月 2019</a><span class="archive-list-count">1</span></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2019/01/">一月 2019</a><span class="archive-list-count">1</span></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2018/12/">十二月 2018</a><span class="archive-list-count">6</span></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2018/11/">十一月 2018</a><span class="archive-list-count">1</span></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2018/08/">八月 2018</a><span class="archive-list-count">1</span></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2018/07/">七月 2018</a><span class="archive-list-count">2</span></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2018/03/">三月 2018</a><span class="archive-list-count">3</span></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2018/02/">二月 2018</a><span class="archive-list-count">2</span></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2018/01/">一月 2018</a><span class="archive-list-count">2</span></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2017/12/">十二月 2017</a><span class="archive-list-count">1</span></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2017/10/">十月 2017</a><span class="archive-list-count">1</span></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2017/09/">九月 2017</a><span class="archive-list-count">1</span></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2017/06/">六月 2017</a><span class="archive-list-count">7</span></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2016/12/">十二月 2016</a><span class="archive-list-count">4</span></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2016/07/">七月 2016</a><span class="archive-list-count">1</span></li></ul></div></div><div class="widget-wrap"><h3 class="widget-title">标签</h3><div class="widget"><ul class="tag-list"><li class="tag-list-item"><a class="tag-list-link" href="/tags/Alibaba/">Alibaba</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Docker/">Docker</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Git/">Git</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Idea/">Idea</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Jackson/">Jackson</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Java基础/">Java基础</a><span class="tag-list-count">4</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Lombok/">Lombok</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Nginx/">Nginx</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Oracle/">Oracle</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Quartz/">Quartz</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Redis/">Redis</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Session/">Session</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Shiro/">Shiro</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Spring/">Spring</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/SpringBoot/">SpringBoot</a><span class="tag-list-count">7</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Storm/">Storm</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Swagger/">Swagger</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Tomcat/">Tomcat</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Zookeeper/">Zookeeper</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/前后端分离/">前后端分离</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/多数据源/">多数据源</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/工作流/">工作流</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/工具/">工具</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/开发工具框架/">开发工具框架</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/微服务/">微服务</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/数据库/">数据库</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/负载均衡/">负载均衡</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/集群/">集群</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/面试题/">面试题</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/项目架构/">项目架构</a><span class="tag-list-count">2</span></li></ul></div></div><div class="widget-wrap"><h3 class="widget-title">标签云</h3><div class="widget tagcloud"> <a href="/tags/Alibaba/" style="font-size:10px">Alibaba</a> <a href="/tags/Docker/" style="font-size:15px">Docker</a> <a href="/tags/Git/" style="font-size:10px">Git</a> <a href="/tags/Idea/" style="font-size:10px">Idea</a> <a href="/tags/Jackson/" style="font-size:10px">Jackson</a> <a href="/tags/Java基础/" style="font-size:17.5px">Java基础</a> <a href="/tags/Lombok/" style="font-size:10px">Lombok</a> <a href="/tags/Nginx/" style="font-size:15px">Nginx</a> <a href="/tags/Oracle/" style="font-size:10px">Oracle</a> <a href="/tags/Quartz/" style="font-size:10px">Quartz</a> <a href="/tags/Redis/" style="font-size:12.5px">Redis</a> <a href="/tags/Session/" style="font-size:10px">Session</a> <a href="/tags/Shiro/" style="font-size:12.5px">Shiro</a> <a href="/tags/Spring/" style="font-size:12.5px">Spring</a> <a href="/tags/SpringBoot/" style="font-size:20px">SpringBoot</a> <a href="/tags/Storm/" style="font-size:10px">Storm</a> <a href="/tags/Swagger/" style="font-size:12.5px">Swagger</a> <a href="/tags/Tomcat/" style="font-size:12.5px">Tomcat</a> <a href="/tags/Zookeeper/" style="font-size:10px">Zookeeper</a> <a href="/tags/前后端分离/" style="font-size:10px">前后端分离</a> <a href="/tags/多数据源/" style="font-size:10px">多数据源</a> <a href="/tags/工作流/" style="font-size:15px">工作流</a> <a href="/tags/工具/" style="font-size:10px">工具</a> <a href="/tags/开发工具框架/" style="font-size:15px">开发工具框架</a> <a href="/tags/微服务/" style="font-size:10px">微服务</a> <a href="/tags/数据库/" style="font-size:10px">数据库</a> <a href="/tags/负载均衡/" style="font-size:10px">负载均衡</a> <a href="/tags/集群/" style="font-size:10px">集群</a> <a href="/tags/面试题/" style="font-size:10px">面试题</a> <a href="/tags/项目架构/" style="font-size:12.5px">项目架构</a></div></div><div class="widget-wrap widget-list"><h3 class="widget-title">链接</h3><div class="widget"><ul><li> <a href="https://www.xiefayang.com">i蝸居年華_谢谢谢</a></li><li> <a href="http://fullsmilespace.com">微笑空间站</a></li></ul></div></div><div id="toTop" class="fas fa-angle-up"></div></aside></div><footer id="footer"><div class="outer"><div id="footer-info" class="inner"> &copy; 2020 ArtIsLong的博客<br> Powered by <a href="http://hexo.io/" target="_blank">Hexo</a>. Theme by <a href="http://github.com/ppoffice">PPOffice</a></div></div><script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script></footer><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.css"><script src="https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.min.js"></script><script>if(document.getElementById("comments")){var id=decodeURI(window.location.pathname),gitalk=new Gitalk({id:id,owner:"ArtIsLong",repo:"ArtIsLong.github.io",admin:["ArtIsLong"],clientID:"2f97f5d71dbb65bf01e1",clientSecret:"f715a8e1507c1fef1c5e3cfaccef8f59aa734318",distractionFreeMode:!0,createIssueManually:!1,perPage:15});gitalk.render("comments")}</script><script src="/libs/lightgallery/js/lightgallery.min.js"></script><script src="/libs/lightgallery/js/lg-thumbnail.min.js"></script><script src="/libs/lightgallery/js/lg-pager.min.js"></script><script src="/libs/lightgallery/js/lg-autoplay.min.js"></script><script src="/libs/lightgallery/js/lg-fullscreen.min.js"></script><script src="/libs/lightgallery/js/lg-zoom.min.js"></script><script src="/libs/lightgallery/js/lg-hash.min.js"></script><script src="/libs/lightgallery/js/lg-share.min.js"></script><script src="/libs/lightgallery/js/lg-video.min.js"></script><script src="/libs/justified-gallery/jquery.justifiedGallery.min.js"></script><script src="/js/main.js"></script></div><script>((window.gitter={}).chat={}).options={room:"artislong/Lobby"}</script><script src="https://sidecar.gitter.im/dist/sidecar.v1.js" async defer="defer"></script><script type="text/javascript" color="0,0,255" opacity="0.7" zindex="-2" count="99" src="//cdn.bootcss.com/canvas-nest.js/1.0.0/canvas-nest.min.js"></script><script async custom-element="amp-auto-ads" src="https://cdn.ampproject.org/v0/amp-auto-ads-0.1.js"></script></body>